package com.tcfvic.server.tools;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.tcfvic.client.tools.Month;
import com.tcfvic.client.tools.State;

public class CloneTool {

	public static void main (String [] args) { 
		System.out.println(State.valueOf(
				"NSW").getId());
	}


	@SuppressWarnings("unchecked")
	public <T> T cloneObject() throws InstantiationException,
			IllegalAccessException, SecurityException, NoSuchMethodException,
			IllegalArgumentException, InvocationTargetException {
		
		// create new instance
		T newObject = (T) getClass().newInstance();
		Class<? extends Object> c = getClass();
		
		// get all the methods of this class
		Method m[] = c.getDeclaredMethods();
		
		// loop through all the methods
		for (int i = 0; i < m.length; i++) {
			String methodName = m[i].getName();
			
			// if this method starts with get or cloneObject
			if (methodName.startsWith("get") || methodName.equals("cloneObject")) {
				
				// get the method that we have only in string form
				Method getMethod = c.getMethod(methodName);
				System.out.println(getMethod.getName());
				// get the returned value of this method
				Object value = null;
				
				// if the object returned by this get method contains our "cloneObject" method
				// we know it is one of our Hibernate pojos, so clone it
				if (containsCloneMethod(getMethod.getClass().getDeclaredMethods())) { 
					Method cloneMethod = getMethod.getClass().getMethod("cloneObject");
					value = cloneMethod.invoke(getMethod.invoke(this));
				} else {
					value = getMethod.invoke(this);
				}
//				System.out.println(value);
				Method setMethod = c.getMethod("set" + methodName.substring(3),
						getMethod.getReturnType());
				setMethod.invoke(newObject, value);
			}
		}
		return newObject;
	}
	
	private boolean containsCloneMethod(Method [] methods) {
		for (Method method : methods) {
			if (method.getName().equals("cloneObject")) {
				return true;
			}
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	public static <T> T clone(T o) {
		T clone = null;

		try {
			clone = (T) o.getClass().newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}

		// Walk up the superclass hierarchy
		for (Class obj = o.getClass(); !obj.equals(Object.class); obj = obj
				.getSuperclass()) {
			Field[] fields = obj.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				fields[i].setAccessible(true);
				try {
					// for each class/suerclass, copy all fields
					// from this object to the clone
					fields[i].set(clone, fields[i].get(o));
				} catch (IllegalArgumentException e) {
				} catch (IllegalAccessException e) {
				}
			}
		}
		return clone;
	}


	// returns a deep copy of an object
	static public <T> T deepCopy(T oldObj) throws Exception {
		ObjectOutputStream oos = null;
		ObjectInputStream ois = null;
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream(); // A
			oos = new ObjectOutputStream(bos); // B
			// serialize and pass the object
			oos.writeObject(oldObj); // C
			oos.flush(); // D
			ByteArrayInputStream bin = new ByteArrayInputStream(bos
					.toByteArray()); // E
			ois = new ObjectInputStream(bin); // F
			// return the new object
			return (T) ois.readObject(); // G
		} catch (Exception e) {
			System.out.println("Exception in ObjectCloner = " + e);
			throw (e);
		} finally {
			oos.close();
			ois.close();
		}
	}

}

